<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="author" content="wangbo">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

  <div id="app"></div>
  <!-- vue2 响应式的原理 用的是Object.defineProperty -->
  <script>

    class Watcher {
      constructor (vm, key, cb) {
        this.vm = vm;
        // 执行this.getter()，就可以读取data.a.b.c的内容
        this.key = key;
        this.cb = cb;
        Dep.target = this;
        this.vm[this.key];
        Dep.target = null;

      }

      update () {
        this.cb.call(this.vm,  this.vm[this.key]);
      }
    }


    class Dep {
      static target = null
      constructor () {
        this.subs = [];
      }

      addSub (sub) {
        this.subs.push(sub);
      }

      removeSub (sub) {
        remove(this.subs, sub);
      }

      depend (target) {
        this.addSub(window.target);
      }

      notify () {
        this.subs.forEach((sub) => {
          sub.update();
        })
      }
    }

    function remove (arr, item) {
      if (arr.length) {
        const index = arr.indexOf(item);
        if (index > -1) {
          return arr.splice(index, 1);
        }
      }
    }


    function defineReactive (data, key, val) {
      // 新增，递归子属性
      if (typeof val === "object") {
        new Observer(val);
      }
      let dep = new Dep();
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function () {
          dep.depend();
          return val;
        },
        set: function (newVal) {
          if (val === newVal) {
            return;
          }

          val = newVal;
          dep.notify();
        },
      });
    }




    class Observer {
      constructor (value) {
        this.value = value;
        if (!Array.isArray(value)) {
          this.walk(value);
        }
      }

      /**
       * walk会将每一个属性都转换成getter/setter的形式来侦测变化
       * 这个方法只有在数据类型为Object时被调用
       */
      walk (obj) {
        const keys = Object.keys(obj);
        for (let i = 0; i < keys.length; i++) {
          defineReactive(obj, keys[i], obj[keys[i]]);
        }
      }
    }

    class Vue {
      constructor (options = {}) {
        this.el = options.el
        this.data = options.data
        let observer = new Observer(options.data) //监听数据
        new Watcher(this, options.exp, function (val) { //创建watcher实例，调用构造函数。
          el.innerHTML = val
        })
        return this
      }
    }
    let vm = new Vue({
      el: "#app",
      data: {
        name: "张三"
      },
      exp: 'name'
    })
  </script>
</body>

</html>